package com.kgd.news.wxpay.util;

import com.alibaba.fastjson.JSONObject;
import com.jfinal.core.JFinal;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.IAtom;
import com.jfinal.plugin.ehcache.CacheKit;
import com.kgd.news.entity.*;
import com.kgd.news.utils.*;
import com.kgd.news.utils.cache.DbCacheFindUtls;
import com.kgd.news.utils.quartz.CheckWithdrawSystemError;
import com.kgd.news.wxpay.constant.Constant;
import com.kgd.news.wxpay.controller.PayControllerUtil;
import com.kgd.news.wxpay.controller.TransferController;
import org.apache.log4j.Logger;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;


public class RedPacketSplitUtil {
    private static Logger log = Logger.getLogger(RedPacketSplitUtil.class);


    public static int[] splitRedPacket(int ordercount, int money) {
        // TODO Auto-generated method stub
        int[] result = new int[ordercount];
        if (ordercount * 1 > money) {    //若每个人不能分至少一分钱，则显示金额太少，结束程序
            System.out.println("钱太少了，多发点给朋友们吧");
            return result;
        }
        result = allocateRedPacket(ordercount, money);   //乘以100，把元转换为分
        for (int i = 0; i < result.length; i++) {
            System.out.println("分钱为" + result[i]);
            if (result[i] == 0) {
                log.error("出现异常");
                System.out.println("出现异常");
            }
        }
        return result;
    }

    private static int[] allocateRedPacket(int n, int money) {
        // TODO Auto-generated method stub
        int[] result = new int[n];
        double[] randNumber = new double[n];
        int randSum = 0;   //随机数和
        int allocateSum = money - n;    //按每人一分钱的计划扣出来，剩下的再分配
        for (int i = 0; i < randNumber.length; i++) {
            randNumber[i] = Math.random() * allocateSum;    //生成随机数
            randSum += randNumber[i];
        }
        int left = allocateSum;
        for (int i = 0; i < result.length - 1; i++) {
//			round的话，最后一个有可能为0
            result[i] = (int) Math.floor(randNumber[i] / randSum * allocateSum);
            left -= result[i];
            result[i] = result[i] + 1;       //把一分钱加回去
        }
//		有可能最后一个为0
        result[n - 1] = left + 1;

        if (result[n - 1] <= 0) {

        }
        int sum = 0;
        for (int i = 0; i < result.length; i++) {
            sum += result[i];
        }
        if (sum == money) {
            log.trace("整数刚好够");
        }
        return result;
    }

    public static void saveGreetingCard(String orderid, String openid, int split_fee) {
        HkTable hkTable = new HkTable();
        hkTable.setOrderid(orderid);
        hkTable.setBanance(split_fee);
        hkTable.setUserid(openid);
        hkTable.setIssend(Constant.SPLIT_HB_SEND);
        hkTable.save();
    }

    /**
     * @param orderid
     * @param openid
     * @param split_fee 想账户里增减
     */
    public static boolean addCashToUser(String orderid, String openid, int split_fee, String record_attach, int user_cashtype) {
        // TODO: 2018/2/1 这个也要和别的同步吧，防止
//		这个应该通过userid同步吧
        boolean isSuccess = false;
        if (!StringUtilsWx.inPool(openid)) {
//                orderid.intern()可以获得常量池里的
            String s2 = openid.intern();
            openid = s2;
        }
        synchronized (openid) {
            SysUser sysUser = SysUser.dao.findFirst("select * from sys_user where userid = ?", openid);
            if (sysUser != null) {
                int balance = sysUser.getBalance();
                if (user_cashtype == Constant.USER_CASHTYPE_EXPEND) {
                    if (balance - split_fee >= 0) {
                        sysUser.setBalance(balance - split_fee);
                    } else {
//                        账户余额不足的,返回错误也没有用呀，钱已经用了，先将账户清零，防止下次再提现
                        // TODO: 2018/2/11 记录错误的信息，可以到数据库里，或者记录在本地
                        sysUser.setBalance(0);
                        return false;
                    }
                } else {
                    sysUser.setBalance(balance + split_fee);
                }
                isSuccess = sysUser.update();
                if (user_cashtype == Constant.USER_CASHTYPE_EXPEND) {
                    OrderUtils.recordUserCash(-split_fee, orderid, openid, balance - split_fee, record_attach, user_cashtype);
                } else {
                    OrderUtils.recordUserCash(split_fee, orderid, openid, balance + split_fee, record_attach, user_cashtype);
                }
//               清楚缓存吧
                DbCacheFindUtls.removeSysUser(openid);
            } else {
            }
        }
        return isSuccess;
    }

    /**
     * @param orderid
     * @param openid
     * @param split_fee 想账户里增减
     */
    public static boolean addCashToUserWithoutSych(String orderid, String openid, int split_fee, String record_attach, int user_cashtype) {
        boolean isSuccess = false;
        SysUser sysUser = SysUser.dao.findFirst("select * from sys_user where userid = ?", openid);
        if (sysUser != null) {
            int balance = sysUser.getBalance();
            if (user_cashtype == Constant.USER_CASHTYPE_EXPEND) {
                if (balance - split_fee >= 0) {
                    sysUser.setBalance(balance - split_fee);
                } else {
                    sysUser.setBalance(0);
//                        账户余额不足的
                    return false;
                }

            } else {
                sysUser.setBalance(balance + split_fee);
            }
            isSuccess = sysUser.update();
            if (user_cashtype == Constant.USER_CASHTYPE_EXPEND) {
                OrderUtils.recordUserCash(-split_fee, orderid, openid, balance - split_fee, record_attach, user_cashtype);
            } else {
                OrderUtils.recordUserCash(split_fee, orderid, openid, balance + split_fee, record_attach, user_cashtype);
            }
            DbCacheFindUtls.removeSysUser(openid);

//            OrderUtils.recordUserCash(split_fee, orderid, openid, balance + split_fee, record_attach, user_cashtype);
        } else {
        }
        return isSuccess;
    }

    /**
     * @param orderid
     * @param openid
     * @param split_fee 向用户
     */
    public static boolean addCashToUserAndSavAutoWithdraw(String orderid, String openid, int split_fee, String record_attach, int user_cashtype) {
        // TODO: 2018/2/1 这个也要和别的同步吧，防止
//		这个应该通过userid同步吧
        boolean isSuccess = false;
        if (!StringUtilsWx.inPool(openid)) {
//                orderid.intern()可以获得常量池里的
            openid = openid.intern();
        }
        synchronized (openid) {
            String finalOpenid = openid;
            isSuccess = Db.tx(new IAtom() {
                @Override
                public boolean run() throws SQLException {
                    SysUser sysUser = SysUser.dao.findFirst("select * from sys_user where userid = ?", finalOpenid);
                    if (sysUser != null) {
                        int balance = sysUser.getBalance();
                        if (user_cashtype == Constant.USER_CASHTYPE_EXPEND) {
                            sysUser.setBalance(balance - split_fee);
                        } else {
                            sysUser.setBalance(balance + split_fee);
                        }
                        if (sysUser.update()) {
                            OrderUtils.recordUserCash(split_fee, orderid, finalOpenid, balance + split_fee, record_attach, user_cashtype);
                            return true;
                        } else {
                            return false;
                        }
                    } else {
                    }
                    return true;
                }
            });

        }
        return isSuccess;
    }

    /**
     * 检查红包是否超时，如果超时的话，返还给
     * 剩下来的就是去日志了，测试了，还有需要回滚吗
     * 提现按照orderid区分去提
     */
    // TODO: 2018/2/1 好好想想涉及到钱的问题咋样保证呢
    public static void checkSplitCashOutOfTime() {
        long timeOut = System.currentTimeMillis() - Constant.SPLIT_HB_TIME_EXPIRE * 60 * 60 * 1000L;
        List<SplitHb> splitHbList = SplitHb.dao.find("select * from split_hb where state =?  and createtime < ? group by orderid", Constant.SPLIT_HB_STATE_NEW, timeOut);
        for (SplitHb splitHb : splitHbList
                ) {
            String orderid = splitHb.getOrderid();
            String userid = splitHb.getUserid();
            List<SplitHb> splitHbListOrderid = SplitHb.dao.find("select * from split_hb where orderid = ? ", orderid);
            if (splitHbListOrderid != null) {
                int totalfee = 0;
                for (SplitHb splitHbOrderid : splitHbListOrderid
                        ) {
//                        红包过期，未打开，则提现
                    int state = splitHbOrderid.getState();
                    if (timeOut >= splitHbOrderid.getCreatetime() && (state == Constant.SPLIT_HB_STATE_NEW)) {
                        splitHbOrderid.setState(Constant.SPLIT_HB_STATE_OUTOFTIME);
                        splitHbOrderid.update();
                        totalfee += splitHbOrderid.getHbfee();
                    }
                }
                String record_attach = "超过24小时无人领取红包，退款";
                addCashToUser(orderid, userid, totalfee, record_attach, Constant.USER_CASHTYPE_INCOME);
            }
        }
    }

    public static JSONObject getSplitSend(String userid, JSONObject jsonObject, int issend_int) {
        List<HkTable> hkTableList = DbCacheFindUtls.getHkTables(userid, issend_int);
        int hbCount = 0;
        int hbAmounts = 0;
        for (HkTable hkTable : hkTableList
                ) {
            if (hkTable.getHashb() == Constant.GREETCARD_HAS_HB) {
                hbAmounts += hkTable.getBanance();
                hbCount++;
            }
        }
        jsonObject.put("issend", Constant.SPLIT_HB_GET);
        jsonObject.put("hbCount", hbCount);
        jsonObject.put("hbAmounts", hbAmounts);
        jsonObject.put("hkTableList", hkTableList);
        return jsonObject;
    }

    /**
     * @param userid
     * @return
     */
    public static JSONObject getSplitGet(String userid, JSONObject jsonObject) {
        List<HkTable> hkTableList = DbCacheFindUtls.getHkTablesGet(userid);
        int hbCount = 0;
        int hbAmounts = 0;
        List<SplitHb> splitHbList = DbCacheFindUtls.getSplitHbByReceiver(userid);
        for (SplitHb bean : splitHbList
                ) {
            if (bean.getState() == Constant.SPLIT_HB_STATE_OPENED) {
                hbCount++;
                hbAmounts += bean.getHbfee();
            }
        }
        jsonObject.put("hkTableList", hkTableList);
        jsonObject.put("hbCount", hbCount);
        jsonObject.put("hbAmounts", hbAmounts);
        jsonObject.put("issend", Constant.SPLIT_HB_GET);
        return jsonObject;
    }

    /**
     * 提现接口
     *
     * @param openid
     * @param moneyNum
     * @param isAutoWithdrawCash
     * @return
     */
    public static JSONObject withdrawCash(String addressIp, String openid, int moneyNum, boolean isAutoWithdrawCash) {
        boolean isSuccess = false;
        if (!StringUtilsWx.inPool(openid)) {
//                orderid.intern()可以获得常量池里的
            openid = openid.intern();
        }
        synchronized (openid) {
            String finalOpenid = openid;
//            isSuccess = Db.tx(new IAtom() {
//                @Override
//                public boolean run() throws SQLException {
            SysUser sysUser = SysUser.dao.findFirst("select * from sys_user where userid ='" + finalOpenid + "'");
            if (sysUser != null) {
                String partner_trade_no = PayUtil.getTransferNo();
                String nonce_str = PayUtil.getNonceStr();
                WithdrawCash withdrawCash = new WithdrawCash();
                withdrawCash.setUserid(finalOpenid);
                withdrawCash.setMoneyNum(moneyNum);
                withdrawCash.setWithdwawOrderid(partner_trade_no);
                withdrawCash.setState(Constant.WITHDRAW_DOING);
                withdrawCash.setWithdrawTime(DateUtils.getOrderformat());
                int userBalance = sysUser.getBalance();
//            这里也要防止重复点击吧,先要判断账户余额是否足够呀
                if (userBalance >= moneyNum) {
                    JSONObject json = TransferController.transferPay(addressIp, finalOpenid, moneyNum, partner_trade_no, nonce_str);
//                   如果为空的话，就不会兼容了吧
                    if ("SUCCESS".equals(json.get("result_code"))) {
                        return dealWithdrawSuccess(json, finalOpenid, moneyNum, withdrawCash);
                    } else {
                        return dealWithdrawError(json, finalOpenid, moneyNum, withdrawCash, isAutoWithdrawCash, partner_trade_no, nonce_str, addressIp);
                    }
                } else {
                    withdrawCash.save();
                    return new Api(Api.A_202, "cash_amounts参数不对，余额不足").get();
                }
            } else {
                return (new Api(Api.A_202, "用户为空").get());
            }
        }
    }

    private static JSONObject dealWithdrawError(JSONObject json, String finalOpenid, int moneyNum, WithdrawCash withdrawCash, boolean isAutoWithdrawCash, String partner_trade_no, String nonce_str, String addressIp) {
        //                充钱失败了，扣钱
//                        如果这个过程中同一个账号又充钱了咋么办，或者正好自动退款了。
        // TODO: 2018/2/1 这里的同步要做好。和自动返现的代码要做好同步，包括支付
//                        不管成功失败都要记录提现记录表
//                        都是会为空的
        String err_code = (String) json.get("err_code");
        String err_code_des = (String) json.get("err_code_des");
        withdrawCash.setErrCode(err_code);
        withdrawCash.setErrCodeDes(err_code_des);
        if ("NOTENOUGH".equals(err_code)) {
//                    如果是账户余额不足的话，
            boolean isAutoSuccess = true;
            if (!isAutoWithdrawCash) {
                boolean transactionSuccess = RedPacketSplitUtil.addCashToUserWithoutSych(partner_trade_no, finalOpenid, moneyNum, "提现,但未成功，自动提现任务已保存", Constant.USER_CASHTYPE_EXPEND);
                if (transactionSuccess) {
                    AutoWithdraw autoWithdraw = new AutoWithdraw();
                    autoWithdraw.setCreatetime(System.currentTimeMillis());
                    autoWithdraw.setState(Constant.AUTO_WITHDRAW_FAIL);
                    autoWithdraw.setOrderid(partner_trade_no);
                    autoWithdraw.setWithdrawFee(moneyNum);
                    autoWithdraw.setUserid(finalOpenid);
                    isAutoSuccess = autoWithdraw.save();
                } else {
                    log.error("请求提现成功，数据库写入错误：openid=" + finalOpenid + "cash_amounts=" + moneyNum);
                    return new Api(Api.L_206, "请求提现成功，数据库写入错误").get();
                }
//                      这个账户里也要扣钱的
                withdrawCash.setState(Constant.WITHDRAW_FAIL_NOFUNDS);
                boolean isSucess = withdrawCash.save();
                if (isAutoSuccess) {
//                    余额不足，账户余额已扣除，7天内自动到账
                    return (new Api(Api.S_200, "请求成功").get());
                } else {
//                    余额不足，严重错误，数据库未写入
                    return (new Api(Api.L_206, "请求成功").get());
                }
            } else {
//                是后台自动提现的，错误的化，那就直接返回，NOTENOUGH
                return (new Api(Api.L_206, "余额不足").get());
            }
        } else {
            if ("SYSTEMERROR".equals(err_code)) {
//                                这个概率很低吧
//                                系统繁忙，用订单号重试多次都返回成功的话，才算成功，每次隔0.5s中。
                int trycount = 2;
                while (trycount-- > 0) {
                    int status = withdrawCashWherSystemError(addressIp, finalOpenid, moneyNum, partner_trade_no, nonce_str);
                    if (status == Constant.WITHDRAWCASHWHERSYSTEMERROR_SUCCESS) {
//                        状态成功，就要去减去余额吧
                        return (new Api(json).get());
                    } else if (status == Constant.WITHDRAWCASHWHERSYSTEMERROR_SYSTEMERROR) {
//                        循环下一次，这个低概率事件就不处理了
                    } else {
//                        付款失败了。
                        withdrawCash.setState(Constant.WITHDRAW_FAIL);
                        withdrawCash.save();
                        return (new Api(Api.L_203, json).get());
                    }
                }
//                如果尝试5次都是系统错误，返回错误，将错误信息存进数据库里，每1小时查询一次,如果失败了，那数据是正常的，
// 如果成功了就要去从用户账户余额里面扣除，
                withdrawCash.setState(Constant.WITHDRAW_FAIL);
                withdrawCash.save();
                WxPayCheckUtil.addWxPaySystemError(moneyNum, partner_trade_no);
//                数据库记录错误
                return (new Api(Api.L_203, json).get());
//                                如果5次之后还是system_errod，那么就要去
            } else {
                withdrawCash.setState(Constant.WITHDRAW_FAIL);
                withdrawCash.save();
                return (new Api(Api.L_203, json).get());
            }
        }
    }

    private static JSONObject dealWithdrawSuccess(JSONObject json, String finalOpenid, int moneyNum, WithdrawCash withdrawCash) {
        String partner_trade_noid = (String) json.get("partner_trade_no");
//                    这里也去判断一下余额的，也是有
        boolean transactionSuccess = RedPacketSplitUtil.addCashToUserWithoutSych(partner_trade_noid, finalOpenid, moneyNum, "提现", Constant.USER_CASHTYPE_EXPEND);
        if (transactionSuccess) {
            withdrawCash.setState(Constant.WITHDRAW_SUCCESS);
            withdrawCash.setPartnerTradeNo(partner_trade_noid);
            withdrawCash.setPaymentNo((String) json.get("payment_no"));
            withdrawCash.setWithdrawTime((String) json.get("payment_time"));
            if (!withdrawCash.save()) {
            }
            return new Api(json).get();
        } else {
            log.error("请求提现成功，数据库写入错误：openid=" + finalOpenid + "cash_amounts=" + moneyNum);
            return new Api(Api.L_206, "请求提现成功，数据库写入错误").get();
        }
//                            renderJson后面不会中断的
    }

    private static int withdrawCashWherSystemError(String addressIp, String finalOpenid, int moneyNum, String partner_trade_no, String nonce_str) {
        try {
//          每次微信后台系统错误时,查询间隔上个0.2秒
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        JSONObject json = TransferController.transferPay(addressIp, finalOpenid, moneyNum, partner_trade_no, nonce_str);
        if ("SUCCESS".equals(json.get("result_code"))) {
            return Constant.WITHDRAWCASHWHERSYSTEMERROR_SUCCESS;
        } else {
            if ("SYSTEMERROR".equals(json.get("err_code"))) {
                return Constant.WITHDRAWCASHWHERSYSTEMERROR_SYSTEMERROR;
            } else {
                return Constant.WITHDRAWCASHWHERSYSTEMERROR_FAIL;
            }
        }
    }


    /**
     * 自动提现接口
     *
     * @param openid
     * @param moneyNum
     * @param isAutoWithdrawCash
     * @return
     */
    public static JSONObject withdrawCashAuto(String addressIp, String openid, int moneyNum, boolean isAutoWithdrawCash) {
        boolean isSuccess = false;
        if (!StringUtilsWx.inPool(openid)) {
//                orderid.intern()可以获得常量池里的
            openid = openid.intern();
        }
        synchronized (openid) {
            String finalOpenid = openid;
            String partner_trade_no = PayUtil.getTransferNo();
            String nonce_str = PayUtil.getNonceStr();
            WithdrawCash withdrawCash = new WithdrawCash();
            withdrawCash.setUserid(finalOpenid);
            withdrawCash.setMoneyNum(moneyNum);
            withdrawCash.setWithdwawOrderid(partner_trade_no);
            withdrawCash.setState(Constant.WITHDRAW_DOING);
            withdrawCash.setWithdrawTime(DateUtils.getOrderformat());
//            这里也要防止重复点击吧,
            JSONObject json = TransferController.transferPay(addressIp, finalOpenid, moneyNum, partner_trade_no, nonce_str);
            if ("SUCCESS".equals(json.get("result_code"))) {
                String partner_trade_noid = (String) json.get("partner_trade_no");
                withdrawCash.setState(Constant.WITHDRAW_SUCCESS);
                withdrawCash.setPartnerTradeNo(partner_trade_noid);
                withdrawCash.setPaymentNo((String) json.get("payment_no"));
                withdrawCash.setWithdrawTime((String) json.get("payment_time"));
                if (!withdrawCash.save()) {
                }
                return new Api(json).get();
//                            renderJson后面不会中断的
            } else {
//                        不管成功失败都要记录提现记录表
                String err_code = (String) json.get("err_code");
                String err_code_des = (String) json.get("err_code_des");
                withdrawCash.setErrCode(err_code);
                withdrawCash.setErrCodeDes(err_code_des);
                if ("NOTENOUGH".equals(err_code)) {
//                    如果是账户余额不足的话，
//                    这个账户里也要扣钱的
                    withdrawCash.setState(Constant.WITHDRAW_FAIL_NOFUNDS);
                    boolean isSucess = withdrawCash.save();
//                    这里提现就没有成功，
                    return (new Api(Api.S_200, "余额不足，账户余额已扣除，7天内自动到账").get());
                } else {
                    if ("SYSTEMERROR".equals(err_code)) {
                        int status = withdrawCashWherSystemError(addressIp, finalOpenid, moneyNum, partner_trade_no, nonce_str);
                        if (status == Constant.WITHDRAWCASHWHERSYSTEMERROR_SUCCESS) {
//                                        状态成功，自动支付余额已减
                            return new Api(json).get();
                        } else if (status == Constant.WITHDRAWCASHWHERSYSTEMERROR_SYSTEMERROR) {
//                            付款系统又错误了
//                            withdrawCash.setState(Constant.WITHDRAW_FAIL);
//                            withdrawCash.save();
                            return (new Api(Api.L_203, json).get());
                        } else {
//                                        付款失败了。
                            withdrawCash.setState(Constant.WITHDRAW_FAIL);
                            withdrawCash.save();
                            return (new Api(Api.L_203, json).get());
                        }
                    } else {
                        withdrawCash.setState(Constant.WITHDRAW_FAIL);
                        withdrawCash.save();
                        return (new Api(Api.L_203, json).get());
                    }
                }
            }
        }

    }


}
